/* Sentinel - IRC Statistical and Operator Services
** evh.c - Event handlers
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: evh.c 7 2010-02-01 19:17:42Z dubkat $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "event.h"
#include "s_string.h"
#include "slog.h"

static void evh_CheckRehash(void *unused);
static void evh_StatsSave(void *unused);
#ifdef DIE_ON_MASS_COLLIDE
static void evh_ResetCollides(void *unused);
#endif
static void evh_Ping(void *unused);
static void evh_HTML(void *unused);
static void evh_DataDump(void *unused);
static void evh_CheckMidnight(void *unused);
#if defined(SPLITSERV)
static void evh_CheckDelayed(void *unused);
#endif
#ifdef SPLITSERV
static void evh_ExpireSplits(void *unused);
static void evh_DumpMap(void *unused);
static void evh_RemindSplits(void *unused);
#endif
static void evh_Delayed(void *unused);
static void evh_FlushLogs(void *unused);
#ifdef JUPESERV
static void evh_ExpireJupeVotes(void *unused);
#endif

/* This function installs all continuously running events and sets
** up the handler for "delayed" events
*/
void StartInitialEvents(void)
{
  eventAdd("check rehash", evh_CheckRehash, NULL, REHASHINTERVAL);
  eventAdd("stats save", evh_StatsSave, NULL, STATSSAVETIME);
#ifdef DIE_ON_MASS_COLLIDE

  eventAdd("reset collisions", evh_ResetCollides, NULL, COLLIDETIME);
#endif

  eventAdd("uplink ping", evh_Ping, NULL, PINGDELAY);

  /* Once per second (by default) for now, see what the performance
  ** hit would be
  */
  eventAdd("check midnight", evh_CheckMidnight, NULL, MIDNIGHTINTERVAL);
#if defined(SPLITSERV)

  eventAdd("check delayed", evh_CheckDelayed, NULL, sp.delay);
#endif
#if defined(SPYMODE) || defined(ALERTMODE) || defined(DRONES_CHANNEL)

  eventAdd("check admin delayed", evh_CheckAdDelayed, NULL, DEFAULT_DELAY);
#endif
#if defined(TRACK_CTCPVERSION)

  eventAdd("check monserv delayed", evh_CheckMnDelayed, NULL, VERSION_DELAY);
#endif
#ifdef SPLITSERV

  eventAdd("expire splits", evh_ExpireSplits, NULL, EXPIREPOLL);
#endif

  eventAdd("flush logfiles", evh_FlushLogs, NULL, LOGFLUSHTIME);

  eventAdd("delayed events", evh_Delayed, NULL, DEFAULT_DELAY);
}

void FlushEvents(void)
{
  eventDelete(evh_CheckRehash, NULL);
  eventDelete(evh_StatsSave, NULL);
#ifdef DIE_ON_MASS_COLLIDE

  eventDelete(evh_ResetCollides, NULL);
#endif

  eventDelete(evh_Ping, NULL);
#if defined(SPLITSERV)

  eventDelete(evh_CheckDelayed, NULL);
#endif
#if defined(SPYMODE) || defined(ALERTMODE) || defined(DRONES_CHANNEL)

  eventDelete(evh_CheckAdDelayed, NULL);
#endif
#if defined(TRACK_CTCPVERSION)

  eventDelete(evh_CheckMnDelayed, NULL);
#endif
#ifdef SPLITSERV

  eventDelete(evh_ExpireSplits, NULL);
#endif

  eventDelete(evh_CheckMidnight, NULL);
  eventDelete(evh_FlushLogs, NULL);
  eventDelete(evh_Delayed, NULL);
  eventDelete(evh_HTML, NULL);
  eventDelete(evh_DataDump, NULL);
#ifdef SPLITSERV

  eventDelete(evh_DumpMap, NULL);
  eventDelete(evh_RemindSplits, NULL);
#endif
#ifdef JUPESERV

  eventDelete(evh_ExpireJupeVotes, NULL);
#endif
}

void ResetTimedEvents(void)
{
  /* Remove the current calls of these events */
  eventDelete(evh_HTML, NULL);
  eventDelete(evh_DataDump, NULL);
#ifdef SPLITSERV

  eventDelete(evh_DumpMap, NULL);
  eventDelete(evh_RemindSplits, NULL);
#endif
#ifdef JUPESERV

  eventDelete(evh_ExpireJupeVotes, NULL);
#endif
  /* Then restart them with the new times */
  evh_Delayed(NULL);

  /* and finally deal with CheckDelayed... */
#ifdef SPLITSERV

  eventDelete(evh_CheckDelayed, NULL);
  eventAdd("check delayed", evh_CheckDelayed, NULL, sp.delay);
#endif

#if defined(SPYMODE) || defined(ALERTMODE) || defined(DRONES_CHANNEL)

  eventDelete(evh_CheckAdDelayed, NULL);
  eventAdd("check admin delayed", evh_CheckAdDelayed, NULL, DEFAULT_DELAY);
#endif

#if defined(TRACK_CTCPVERSION)

  eventDelete(evh_CheckMnDelayed, NULL);
  eventAdd("check monserv delayed", evh_CheckMnDelayed, NULL, VERSION_DELAY);
#endif
}

static void evh_Delayed(void *unused)
{
  evh_HTML(NULL);
  eventAdd("HTML dump", evh_HTML, NULL, settings.html_time);

  evh_DataDump(NULL);
  eventAdd("data dump", evh_DataDump, NULL, settings.dump_time);

#ifdef SPLITSERV

  evh_DumpMap(NULL);
  eventAdd("dump map text", evh_DumpMap, NULL, sp.text_time);
  if (sp.remind_time > 0)
  {
    evh_RemindSplits(NULL);
    eventAdd("remind splits", evh_RemindSplits, NULL,
             sp.remind_time);
  }
#endif

#ifdef JUPESERV
  evh_ExpireJupeVotes(NULL);
  eventAdd("expire jupe votes", evh_ExpireJupeVotes, NULL, JUPE_EXP_DELAY);
#endif

  /* And remove myself */
  eventDelete(evh_Delayed, NULL);
}

static void evh_CheckRehash(void *unused)
{
  if (need_rehash)
  {
    rehash();
    slog(DEFAULT_LOG, L_NOTICE, "SIGHUP Caught, REHASH completed");
    alert("SIGHUP Caught, REHASH completed");
    sendto_dcc_flag(CF_NOTICES, "SIGHUP Caught, REHASH completed");
    need_rehash = 0;
    wallops(settings.name, "SIGHUP Caught, REHASH completed");
  }
}

static void evh_StatsSave(void *unused)
{
  SaveStats();
}

#ifdef DIE_ON_MASS_COLLIDE
static void evh_ResetCollides(void *unused)
{
  /* netinfo.collides = 0; */
  /* For better "security", I'll simply decrement it */
  if (netinfo.collides > 0)
    netinfo.collides--;
}
#endif

static void evh_Ping(void *unused)
{
  /* XXX                   vv shouldn't happen vv */
  if ((me.uplink != NULL) && (servsock != -1))
    sts("PING %s", me.uplink->name);
}

static void evh_HTML(void *unused)
{
  if (IsHtml())
  {
    DumpHTML();
    spy("HTML Dump Completed");
    sendto_dcc_flag(CF_MISC, "HTML Dump Completed");
  }
}

static void evh_DataDump(void *unused)
{
  if (IsDump())
  {
    datadump(settings.dump_file);
    spy("Data Dump Completed");
    sendto_dcc_flag(CF_MISC, "Data Dump Completed");
  }
}

static void evh_CheckMidnight(void *unused)
{
  static int midnight = 0;

  if (!midnight && is_midnight())
  {
    /* Perform all rollover tasks */
    debug("Its midnight!!! -> %s", sctime(CurrentTime));
    spy("Local Midnight (or close to it) Rollover");
    sendto_dcc_flag(CF_MISC,
                    "Local Midnight (or close to it) Rollover");
    /* Reset Daily Statistics */
    daily.servers = netinfo.servers;
    daily.t_servers = CurrentTime;
    daily.users = netinfo.users;
    daily.t_users = CurrentTime;
    daily.opers = netinfo.opers;
    daily.t_opers = CurrentTime;
    daily.channels = netinfo.channels;
    daily.t_channels = CurrentTime;
    daily.connections = 0;  /* Reset */
    /* end */
    ResetLogs();

    /* Then to make sure it doesn't get run again... */
    midnight = 1;
  }
  else
  {
    if (midnight && !(is_midnight()))
      midnight = 0;
  }
}

#if defined(SPLITSERV)
static void evh_CheckDelayed(void *unused)
{
  is_delayed = 0;
  eventDelete(evh_CheckDelayed, NULL);
}
#endif

#if defined(SPYMODE) || defined(ALERTMODE) || defined(DRONES_CHANNEL)
void evh_CheckAdDelayed(void *unused)
{
  ad_delayed = 0;
  eventDelete(evh_CheckAdDelayed, NULL);
}
#endif

#if defined(TRACK_CTCPVERSION)
void evh_CheckMnDelayed(void *unused)
{
  dlink_node *dl, *tdl;
  struct User *u;
  int count = 0;

  mn_delayed = 0;
  eventDelete(evh_CheckMnDelayed, NULL);

  /* Run all of the delayed users */
  DLINK_FOREACH_SAFE(dl, tdl, delayed_users.head)
  {
    u = dl->data;

    sts(":%s PRIVMSG %s :\001VERSION\001", MonServ.nick, u->nick);

    dlink_delete(dl, &delayed_users);
    dlink_free(dl);

    count++;

    if (count >= VERSION_PERRUN)
      break;
  }

  if (dlink_length(&delayed_users) > 0)
    eventAdd("run pending VERSION", evh_CheckMnDelayed, NULL, VERSION_INTERVAL);
}
#endif

#ifdef SPLITSERV
static void evh_ExpireSplits(void *unused)
{
  if (IsSPEnableExpire())
    expire_splits();
}

static void evh_RemindSplits(void *unused)
{
  struct SpServer *s = NULL;
  dlink_node *dl;

  if (!IsSPEnableRemind())
    return ;
  if (!IsSPReport())
    return ;
  DLINK_FOREACH(dl, split_table.head)
  {
    s = (struct SpServer *) dl->data;
    notice(sp.join_chan, SplitServ.nick, "REMINDER: %s split for %s", s->name,
           timediff(CurrentTime - s->ts));
  }
}

static void evh_DumpMap(void *unused)
{
  if (IsSPEnableText())
    dump_map();
}
#endif

#ifdef JUPESERV
static void evh_ExpireJupeVotes(void *unused)
{
  expire_pending_jupe_votes();
}
#endif

static void evh_FlushLogs(void *unused)
{
  FlushLogs();
}

void evh_ConnectServer(void *unused)
{
  /* XXX unused isn't used YET */
  if (servsock > 0)
  {
    shutdown(servsock, SHUT_RDWR);
    close(servsock);
    ServTermination();
    delete_connection(servsock);
  }

  ConnectTo(settings.uplink, settings.port);
  eventDelete(evh_ConnectServer, NULL);
}
